#include <REGX52.H>
#include "SPI.h"
#include "INTRINS.H"
#include "W25Q128.h"
#include "Delay.h"


void W25Q128_Init()
{
    SPI_Init(SPI_Mode_0);
    W25Q128_WakeUp();
    W25Q128_WaitBusy();
    // W25Q128_CheckAndDisableWriteProtect();
    Delay(5);
}

unsigned char W25Q128_ReadSR(unsigned char Reg)
{
    unsigned char command,sr;
    switch(Reg)
    {
        case 1:
            command=W25Q128_CMD_READ_STATUS1;
            break;
        case 2:
            command = W25Q128_CMD_READ_STATUS2;
            break;
        case 3:
            command = W25Q128_CMD_READ_STATUS3;
            break;
        default:
            command = W25Q128_CMD_READ_STATUS1;
            break;
    }
    SPI_CS_Enable();
    SPI_TransFer(command);
    sr=SPI_TransFer(0x00);
    SPI_CS_Disable();
    return sr;
}

void W25Q128_WriteSR(unsigned char Reg,unsigned char Sr)
{
    unsigned char command;
    switch (Reg)
    {
    case 1:
        command = W25Q128_CMD_WRITE_STATUS1;
        break;
    case 2:
        command = W25Q128_CMD_WRITE_STATUS2;
        break;
    case 3:
        command = W25Q128_CMD_WRITE_STATUS3;
        break;
    default:
        command = W25Q128_CMD_WRITE_STATUS1;
        break;
    }

    W25Q128_WriteEnable();

    SPI_CS_Enable();
    SPI_TransFer(command);
    SPI_TransFer(Sr);
    SPI_CS_Disable();

    W25Q128_WaitBusy();
}

void W25Q128_WriteEnable()
{
    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_WRITE_ENABLE);
    SPI_CS_Disable();
}

void W25Q128_WriteDisable()
{
    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_WRITE_DISABLE);
    SPI_CS_Disable();
}

void W25Q128_WaitBusy()
{
    while ((W25Q128_ReadSR(1) & W25Q128_STATUS_BUSY) == W25Q128_STATUS_BUSY)
    {
        Delay(5);
    }
}

unsigned int W25Q128_ReadID()
{
    unsigned int id;
    unsigned char id_h,id_l;

    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_DEVICE_ID);
    SPI_TransFer(0x00);
    SPI_TransFer(0x00);
    SPI_TransFer(0x00);
    id_h=SPI_TransFer(0x00);
    id_l=SPI_TransFer(0x00);
    SPI_CS_Disable();

    id=(id_h<<8)|id_l;
    return id;
}

void W25Q128_UniqueID(unsigned char *ID)
{
    unsigned char i;
    SPI_CS_Enable();
    SPI_TransFer(0x4B);
    for(i=0;i<4;i++)
    {
        SPI_TransFer(0x00);
    }
    for(i=0;i<8;i++)
    {
        ID[i]=SPI_TransFer(0x00);
    }
    SPI_CS_Disable();
}

void W25Q128_PowerDown()
{
    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_POWER_DOWN);
    SPI_CS_Disable();
    Delay(5);
}

void W25Q128_WakeUp()
{
    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_RELEASE_POWER_DOWN);
    SPI_CS_Disable();
    Delay(5);
}

/**
 * @brief 擦除扇区(4KB)
 * @param Sector 扇区地址(0-4095)
 */
void W25Q128_EraseSector(unsigned int Sector)
{
    unsigned int addr=Sector*W25Q128_SECTOR_SIZE;
    
    W25Q128_WriteEnable();
    W25Q128_WaitBusy();

    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_SECTOR_ERASE);
    SPI_TransFer((addr>>16)&0xFF);
    SPI_TransFer((addr>>8)&0xFF);
    SPI_TransFer(addr&0xFF);
    SPI_CS_Disable();

    W25Q128_WaitBusy();
    Delay(5);
}

/**
 * @brief 擦除32K块
 * @param Block 块地址(0-511)
 */
void W25Q128_EraseBlock32K(unsigned int Block)
{
    unsigned int addr=Block*0x8000;

    W25Q128_WriteEnable();
    W25Q128_WaitBusy();

    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_BLOCK_ERASE_32K);
    SPI_TransFer((addr>>16)&0xFF);
    SPI_TransFer((addr>>8)&0xFF);
    SPI_TransFer((addr&0xFF));
    SPI_CS_Disable();

    W25Q128_WaitBusy();
}

/**
 * @brief 擦除64K块
 * @param Block 块地址(0-255)
 */
void W25Q128_EraseBlock64K(unsigned int Block)
{
    unsigned int addr=Block*W25Q128_BLOCK_SIZE;

    W25Q128_WriteEnable();
    W25Q128_WaitBusy();

    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_BLOCK_ERASE_64K);
    SPI_TransFer((addr>>16)&0xFF);
    SPI_TransFer((addr>>8)&0xFF);
    SPI_TransFer(addr&0xFF);
    SPI_CS_Disable();

    W25Q128_WaitBusy();
}

void W25Q128_EraseChip()
{
    W25Q128_WriteEnable();
    W25Q128_WaitBusy();

    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_CHIP_ERASE);
    SPI_CS_Disable();

    W25Q128_WaitBusy();
}

/**
 * @brief 验证扇区是否已擦除（全为0xFF）
 * @param Sector 扇区号
 * @retval 1-已擦除，0-未完全擦除
 */
unsigned char xdata W25Q128_buffer[16];
bit W25Q128_VerifyErase(unsigned int Sector)
{
    unsigned int addr = Sector * W25Q128_SECTOR_SIZE;
    unsigned int i,j;

    // 随机读取扇区内的多个位置检查是否为0xFF
    for (i = 0; i < 4; i++)
    {
        W25Q128_ReadData(addr + i * 1024, W25Q128_buffer, 16);
        for (j = 0; j < 16; j++)
        {
            if (W25Q128_buffer[j] != 0xFF)
                return 0; // 发现非0xFF数据，擦除不彻底
        }
    }

    return 1; // 所有检查点都是0xFF，擦除成功
}

void W25Q128_ReadData(unsigned int Address,unsigned char *Data,unsigned int Length)
{
    unsigned int i;

    W25Q128_WaitBusy();

    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_READ_DATA);
    SPI_TransFer((Address>>16)&0xFF);
    SPI_TransFer((Address>>8)&0xFF);
    SPI_TransFer(Address&0xFF);

    for(i=0;i<Length;i++)
    {
        Data[i]=SPI_TransFer(0x00);
    }
    SPI_CS_Disable();
}

void W25Q128_WritePage(unsigned int Address,unsigned char *Data,unsigned int Length)
{
    unsigned int i;

    if(Length>W25Q128_PAGE_SIZE)
    {
        Length = W25Q128_PAGE_SIZE;
    }
    if ((Address % W25Q128_PAGE_SIZE) + Length > W25Q128_PAGE_SIZE)
    {
        Length = W25Q128_PAGE_SIZE - (Address % W25Q128_PAGE_SIZE);
    }

    W25Q128_WriteEnable();
    W25Q128_WaitBusy();

    SPI_CS_Enable();
    SPI_TransFer(W25Q128_CMD_PAGE_PROGRAM);
    SPI_TransFer((Address>>16)&0xFF);
    SPI_TransFer((Address>>8)&0xFF);
    SPI_TransFer(Address&0xFF);

    for(i=0;i<Length;i++)
    {
        SPI_TransFer(Data[i]);
    }
    SPI_CS_Disable();

    W25Q128_WaitBusy();
}

void W25Q128_WriteData(unsigned int Address,unsigned char *Data,unsigned int Length)
{
    unsigned int page_remain;
    unsigned int write_len;
    unsigned int write_offset=0;

    while(Length>0)
    {
        page_remain=W25Q128_PAGE_SIZE-(Address%W25Q128_PAGE_SIZE);
        write_len=(Length>page_remain)?page_remain:Length;
        W25Q128_WritePage(Address,Data+write_offset,write_len);

        Address+=write_len;
        write_offset+=write_len;
        Length-=write_len;
    }
}

/**
 * @brief 增强型数据写入
 * @param Address 起始地址
 * @param Data 数据指针
 * @param Length 数据长度
 */
void W25Q128_EnhancedWriteData(unsigned int Address, unsigned char *Data, unsigned int Length)
{
    unsigned int page_remain;
    unsigned int write_len;
    unsigned int write_offset = 0;
		unsigned int sector;

    // 计算起始地址所在的扇区
    unsigned int start_sector = Address / W25Q128_SECTOR_SIZE;
    // 计算结束地址所在的扇区
    unsigned int end_sector = (Address + Length - 1) / W25Q128_SECTOR_SIZE;

    // 擦除所有涉及的扇区
    for (sector = start_sector; sector <= end_sector; sector++)
    {
        W25Q128_EraseSector(sector);
        // 验证擦除是否成功
        if (!W25Q128_VerifyErase(sector))
        {
            // 擦除失败，可以重试或报错
            // 这里可以选择重试一次
            W25Q128_EraseSector(sector);
        }
    }

    // 写入数据
    while (Length > 0)
    {
        page_remain = W25Q128_PAGE_SIZE - (Address % W25Q128_PAGE_SIZE);
        write_len = (Length > page_remain) ? page_remain : Length;

        W25Q128_WritePage(Address, Data + write_offset, write_len);

        Address += write_len;
        write_offset += write_len;
        Length -= write_len;
    }
}

/**
 * @brief 检查并解除写保护
 */
void W25Q128_CheckAndDisableWriteProtect(void)
{
    unsigned char sr1, sr2, sr3;

    // 读取状态寄存器
    sr1 = W25Q128_ReadSR(1);
    sr2 = W25Q128_ReadSR(2);
    sr3 = W25Q128_ReadSR(3);

    // 检查是否有写保护位被设置
    if (sr1 & 0x1C) // 检查BP0, BP1, BP2位
    {
        // 解除写保护
        W25Q128_WriteEnable();
        W25Q128_WriteSR(1, sr1 & ~0x1C); // 清除BP0, BP1, BP2位
    }

    if (sr2 & 0x80) // 检查SRP1位
    {
        // 解除写保护
        W25Q128_WriteEnable();
        W25Q128_WriteSR(2, sr2 & ~0x80); // 清除SRP1位
    }

    if (sr3 & 0x7C) // 检查CMP, LB1, LB2, LB3位
    {
        // 解除写保护
        W25Q128_WriteEnable();
        W25Q128_WriteSR(3, sr3 & ~0x7C); // 清除CMP, LB1, LB2, LB3位
    }
}

/**
 * @brief 完整的写入和验证流程
 * @param Address 起始地址
 * @param Data 数据指针
 * @param Length 数据长度
 * @retval 1-成功，0-失败
 */
unsigned char xdata W25Q128_read_back[256];
bit W25Q128_WriteAndVerify(unsigned int Address, unsigned char *Data, unsigned int Length)
{
    unsigned int i, chunk_size;
    unsigned int remaining = Length;
    unsigned int current_addr = Address;

    // 写入数据
    W25Q128_EnhancedWriteData(Address, Data, Length);

    // 等待写入完成
    W25Q128_WaitBusy();
    Delay(5);

    // 分段读取和验证数据（避免使用大数组）
    while (remaining > 0)
    {
        chunk_size = (remaining > sizeof(W25Q128_read_back)) ? sizeof(W25Q128_read_back) : remaining;

        // 读取刚写入的数据
        W25Q128_ReadData(current_addr, W25Q128_read_back, chunk_size);

        // 验证数据
        for (i = 0; i < chunk_size; i++)
        {
            if (W25Q128_read_back[i] != Data[current_addr - Address + i])
            {
                return 0; // 验证失败
            }
        }

        current_addr += chunk_size;
        remaining -= chunk_size;
    }

    return 1; // 验证成功
}
